package cn.daniel.framework.gateway.filter;

import cn.daniel.framework.core.constants.CommonStringConstants;
import cn.daniel.framework.core.constants.ServerHttpRequestHeader;
import cn.daniel.framework.core.enums.IPTypeEnum;
import cn.daniel.framework.core.nacos.GatewayNacosDataIdAndGroupEnum;
import cn.daniel.framework.core.utils.IpUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.Executor;

/**
 * @author daniel.tuen
 * @date 2025/9/1 14:48
 * @description 黑名单
 */
@Slf4j
@Component
@ConditionalOnProperty(
        name = "daniel.enable_black_list",  // 配置项名称
        havingValue = "true",               // 期望的值
        matchIfMissing = false              // 如果配置不存在，是否匹配（false表示不匹配）
)
public class IpBlackAddressFilter implements GlobalFilter, Ordered {

    /**
     *  黑名单存储，使用线程安全的集合
     */
    private final Set<String> ipv4Blacklist = Collections.synchronizedSet(new HashSet<>());
    private final Set<String> ipv6Blacklist = Collections.synchronizedSet(new HashSet<>());

    private final NacosConfigManager nacosConfigManager;

    public IpBlackAddressFilter(NacosConfigManager nacosConfigManager) {
        this.nacosConfigManager = nacosConfigManager;
    }

    /**
     * 初始化方法，从Nacos加载配置并注册监听器
     *
     * {
     *   "IPv4": [
     *     "192.168.1.100",
     *     "10.0.0.*",
     *     "172.16.*.5",
     *     "*.255.255.255",
     *     "192.*.3.4"
     *   ],
     *   "IPv6": [
     *     "2001:db8::1",
     *     "fe80::*",
     *     "*:1234:5678",
     *     "::1",
     *     "2001:*:3:4:5:6:7:8"
     *   ]
     * }
     */
    @PostConstruct
    public void init() {
        try {
            // 初始加载配置
            String config = nacosConfigManager.getConfigService().getConfig(GatewayNacosDataIdAndGroupEnum.IP_BLACKLIST_CONFIG.getDataId(),
                    GatewayNacosDataIdAndGroupEnum.IP_BLACKLIST_CONFIG.getGroup(), 5000);
            refreshBlacklist(config);

            // 注册配置变更监听器
            nacosConfigManager.getConfigService().addListener(GatewayNacosDataIdAndGroupEnum.IP_BLACKLIST_CONFIG.getDataId(),
                    GatewayNacosDataIdAndGroupEnum.IP_BLACKLIST_CONFIG.getGroup(), new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    // 配置变更时刷新黑名单
                    refreshBlacklist(configInfo);
                }

                @Override
                public Executor getExecutor() {
                    return null;  // 使用默认线程池
                }
            });
        } catch (NacosException e) {
            throw new RuntimeException("Failed to initialize Nacos IP blacklist filter", e);
        }
    }

    /**
     * 从配置刷新黑名单
     */
    private void refreshBlacklist(String config) {
        if (config == null || config.trim().isEmpty()) {
            ipv4Blacklist.clear();
            ipv6Blacklist.clear();
            return;
        }

        try {
            // 解析JSON配置
            Map<String, List<String>> blacklistConfig = JSON.parseObject(
                    config, new TypeReference<Map<String, List<String>>>() {});

            // 更新IPv4黑名单
            List<String> ipv4List = blacklistConfig.getOrDefault(IPTypeEnum.IPv4.getCode(), new ArrayList<>());
            ipv4Blacklist.clear();
            ipv4List.forEach(ip -> {
                ipv4Blacklist.add(ip);
            });

            // 更新IPv6黑名单
            List<String> ipv6List = blacklistConfig.getOrDefault(IPTypeEnum.IPv6.getCode(), new ArrayList<>());
            ipv6Blacklist.clear();
            ipv6List.forEach(ip -> {
                ipv6Blacklist.add(ip);
            });

            log.info("IP blacklist refreshed successfully. IPv4 count: {}, IPv6 count: {}",
                    ipv4Blacklist.size(), ipv6Blacklist.size());
        } catch (Exception e) {
            log.error("Failed to parse IP blacklist config: {}" , e.getMessage());
        }
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        /**
         * 1、ipv4 ipv6
         * 2、通配符 192.*; 10.104.7.*
         * 3、开发ip 黑白名单的维护功能
         * 4、在页面维护后，推送至nacos 更新
         */
        String clientIp = getClientIp(request);

        // 验证IP类型并检查是否在黑名单中
        if (IpUtil.isValidIPv4(clientIp) && IpUtil.inBlackList(ipv4Blacklist, clientIp)) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete();
        }

        if (IpUtil.isValidIPv6(clientIp) && IpUtil.inBlackList(ipv6Blacklist, clientIp)) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete();
        }

        // IP不在黑名单中，继续处理请求
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100;
    }

    /**
     * 获取客户端真实IP地址
     */
    private String getClientIp(ServerHttpRequest request) {
        String ip = request.getHeaders().getFirst(ServerHttpRequestHeader.X_FORWARDED_FOR);
        if (ip == null || ip.isEmpty() || CommonStringConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst(ServerHttpRequestHeader.PROXY_CLIENT_IP);
        }
        if (ip == null || ip.isEmpty() || CommonStringConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst(ServerHttpRequestHeader.WL_PROXY_CLIENT_IP);
        }
        if (ip == null || ip.isEmpty() || CommonStringConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst(ServerHttpRequestHeader.HTTP_CLIENT_IP);
        }
        if (ip == null || ip.isEmpty() || CommonStringConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst(ServerHttpRequestHeader.HTTP_X_FORWARDED_FOR);
        }
        if (ip == null || ip.isEmpty() || CommonStringConstants.UNKNOWN.equalsIgnoreCase(ip)) {
            // 直接从请求地址中获取
            ip = request.getRemoteAddress() != null
                    ? request.getRemoteAddress().getAddress().getHostAddress()
                    : CommonStringConstants.UNKNOWN;
        }

        // 处理多个代理的情况，取第一个IP
        if (ip != null && ip.contains(CommonStringConstants.COMMA)) {
            ip = ip.split(CommonStringConstants.COMMA)[0].trim();
        }

        return ip;
    }
}
